﻿using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text.Encodings.Web;
using Common.Util;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;

namespace Common.Authentication;

public class AppJwtAuthenticationHandler : AuthenticationHandler<AppJwtAuthenticationOptions>
{
    public AppJwtAuthenticationHandler(IOptionsMonitor<AppJwtAuthenticationOptions> options, ILoggerFactory logger,
        UrlEncoder encoder) : base(options, logger, encoder)
    {
    }


    private void SaveAuthenticationResult(HttpContext context, AuthenticateResult result)
    {
        // AuthenticationFeatures instance = new AuthenticationFeatures(result);
        var authFeature = new AppAuthFeature() { AuthenticateResult = result };
        context.Features.Set(authFeature);
    }

    private async Task<AuthenticateResult> InternalHandleAuthenticationAsync()
    {
        Request.Headers.TryGetValue(HeaderNames.Authorization, out var values);
        if (values.Count == 0)
        {
            return AuthenticateResult.Fail("Authorization header not found.");
        }

        var value = values.FirstOrDefault();
        if (string.IsNullOrWhiteSpace(value))
        {
            return AuthenticateResult.Fail("Bearer value is empty");
        }

        if (!value.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
        {
            return AuthenticateResult.Fail("Bearer token not found.");
        }

        var token = value.Remove(0, 7);

        try
        {
            TokenUtil.Verify(token, Options.Key);
            var handler = new JwtSecurityTokenHandler();
            var securityToken = handler.ReadJwtToken(token);

            var claimsIdentity =
                new ClaimsIdentity(securityToken.Claims, AppJwtBearerDefaults.AuthenticationScheme, "username", "");
            var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
            var authenticationTicket = new AuthenticationTicket(claimsPrincipal, Scheme.Name);

            var result = AuthenticateResult.Success(authenticationTicket);
            return await Task.FromResult(result);
        }
        catch (Exception ex)
        {
            return AuthenticateResult.Fail("Checkout Token Error:" + ex.Message);
        }
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var authenticateResult = await InternalHandleAuthenticationAsync();
        SaveAuthenticationResult(Context, authenticateResult);
        return authenticateResult;
    }
}